package hirt;




//import java.io.BufferedReader;
import java.io.IOException;
//import java.io.InputStream;
import java.io.OutputStream;
//import java.io.PrintWriter;
//import java.net.DatagramSocket;
//import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
//import java.net.SocketException;
//import java.net.UnknownHostException;
import java.nio.ByteBuffer;
//import java.util.Queue;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Server Parent holds all the members the that need to be accessed from anywhere on the server
 * It also listens for new clients and sets up ServerChildren to handle those clients
 * @author zamille
 */
public class ServerParent1 extends Thread {
        /** List of all sockets conecting to each client */
	protected static Vector<Socket> outSockets = null;
        /** The current session */
        Session gameSession;
        /** The numbers of players that have joined this session */
        private int count;
        /** Thread that automatically iterates the clock when it is nessesary*/
        Thread timeCounter;
        /** Automatically sends any requested vehicles to the scene 5 seconds after they are requested*/
        Thread carArrivals;
        /** A list of all active children*/
        Vector<ServerChild> children;
        /** Whether or not the clock needs to be iterating */
        boolean countTime;
        /** A list of a requested vehicles that have yet to arrive */
        Vector<CreationAction> arrivalQueue;
	//protected static InetAddress group;


    /**
     *
     * @param string
     */
    public ServerParent1(String string) {
		// TODO Auto-generated constructor stub
    	super(string);
        
	}

    /**
     * Initilizes all the variables
     * and starts listening for any clinets that want to join
     * Then sets up serverChildren to deal with those clients
     * @throws IOException
     */
    public void main() throws IOException
    {
        count = 1;
        countTime = false;
    	boolean listening = true;
    	outSockets = new Vector<Socket>();
        children = new Vector<ServerChild>();
        arrivalQueue = new Vector<CreationAction>();
        gameSession = new Session(0);
        //gameSession.initializeScenario();
        gameSession.currentScenario = new Scenario(); //
        gameSession.currentScenario.addItem(gameSession.currentScenario.world);
        //gameSession.currentScenario.loadFromFile(ServerConstants.FILENAME);
        gameSession.loadScenarioList(ServerConstants.SCENAIORFOLDERNAME);
        timeCounter = new Thread() {
            public void run() {
                try {
                    while (true)
                    {
                        if (countTime)
                        {
                            Scenario currentScenario = gameSession.getCurrentScenario();
                            currentScenario.setCurrentTime(currentScenario.getCurrentTime() + 1);
                            if (currentScenario.getCurrentTime()%5 == 0)
                                SendTime();
                        }
                        Thread.sleep(1000);
                    }
                } catch (Exception ex) {

                }
            }
        };



        //System.out.println("about to run");
        timeCounter.start();

         carArrivals = new Thread() {
            public void run() {
                try {
                    Message message;
                    CreationAction action;
                    TransferAction transfer;
                    //Item2D world;
                    while (true)
                    {
                        for (int i = 0; i < arrivalQueue.size(); i++)
                        {
                            System.out.println("Queue exists");
                            action = arrivalQueue.get(i);
                            double time = gameSession.getCurrentScenario().getCurrentTime();
                            //System.out.println(action.startTime + " >= " + time);
                            if (action.startTime+5.0 <= time)
                            {
                                System.out.println("About to send arrival");
                                //world = gameSession.currentScenario.getWorld();
                                
                                if (!gameSession.currentScenario.detectCollision((Item2D) action.actor))
                                {
                                    gameSession.currentScenario.getWorld().putItem(action.actor);
                                    transfer = new TransferAction(action.actor, gameSession.currentScenario.getWorld());
                                    transfer.setStartTime(gameSession.getCurrentScenario().getCurrentTime());
                                    message = new Message(Message.TRANSFER_MESSAGE, transfer);
                                    SendMessage(message);
                                    gameSession.addAction(transfer);
                                    arrivalQueue.remove(i);
                                    if (i == arrivalQueue.size())
                                        break;
                                    else
                                        i--;
                                }
                            }
                            
                        }
                        Thread.sleep(1000);
                    }
                } catch (Exception ex) {

                }
            }
        };
        carArrivals.start();
        //System.out.println("past running");
    	/*try {
		//group = InetAddress.getByName("230.0.0.1");
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/

	ServerSocket inSocket = null;
	try {
		inSocket = new ServerSocket(ServerConstants.PORT);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

	System.out.println("About to listen to music");
    	while (listening)
    	{
    		ServerChild child = new ServerChild(inSocket.accept(),this);  //listens for any client that wants
                children.add(child);                                    // to join the game and starts a child
                child.start();                                          // to handle that client
    		System.out.println("Child # " + count + " has begun, believes the parent");
    		count++;
    	}

    	inSocket.close();
    	Socket outSocket;
        for (int i = 0; i < outSockets.size(); i++)
        {
            outSocket = outSockets.get(i);
            outSocket.close();
        }
    }

    @Override
    public void run()
    {
        try
        {
            main();
        } catch (IOException ex)
        {
            //Logger.getLogger(ServerParent1.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(String[] args) throws IOException
    {
        ServerParent1 serverParent = new ServerParent1( "" );

    	serverParent.main();
    }

    /**
     * Returns a basic an unique player name of the form 'Player 1'
     * @return
     */
    protected String getNextName()
    {
        return "Player " + count;
    }

    /**
     * Resets the number of players whenever all players leave the session
     */
    protected void resetPlayerCount()
    {
        count = 1;
    }

    /**
     * Sends the passed message across the network to every connected client
     * @param inputLine
     * @throws IOException
     */
    protected void SendMessage(Message inputLine) throws IOException
    {
            System.out.println("Child sending message");
            //System.out.println("Message = " + inputLine.content.getMessage());
            ByteBuffer buf = inputLine.toByte();
            //DatagramPacket packet;
            //packet = new DatagramPacket(buf, buf.length, group,4447);

           // Socket outSocket;
            SendMessage(buf);
    }

    /**
     * Sends the passed message across the network to every connected client
     * Except for the client associated with the passed socket
     * If the passed socket is null it sends the message to every client
     * @param buf
     * @param server
     * @throws IOException
     */
    protected void SendMessage(ByteBuffer buf, Socket server) throws IOException
    {
            OutputStream out = null;
            Socket current;
            boolean self = server == null;
            for (int i = 0; i < outSockets.size(); i++)
            {
                current = outSockets.get(i);
                if (self || !current.equals(server))
                {
                    out = outSockets.get(i).getOutputStream();
                    out.write(buf.array());
                }
            }
            System.out.println("Child sent message");
    }

    /**
     * Sends the passed message across the network to every connected client
     * @param buf
     * @throws IOException
     */
    protected void SendMessage(ByteBuffer buf) throws IOException
    {
        SendMessage(buf, null);
    }

    /**
     * Updates the clock of every client with the accurate time
     */
    protected void SendTime()
    {
        Scenario currentScenario = gameSession.currentScenario;
        /*try {
            currentScenario.setCurrentTime(currentScenario.getCurrentTime() + 1);
        } catch (Exception ex) {
            Logger.getLogger(ServerChild.class.getName()).log(Level.SEVERE, null, ex);
        }*/
        Action action = new Action(null, currentScenario.getCurrentTime(), 0);
        Message message = new Message(Message.REFRESH_TIME_MESSAGE, action);
        try {
            SendMessage(message);
        } catch (IOException ex) {
            Logger.getLogger(ServerChild.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    /**
     * Informs each client of what item is associated with they're local player
     */
    protected void assignPlayers()
    {
        for (int i = 0; i < children.size(); i++) {
            children.get(i).sendPlayerAssignment();

        }
    }

    /**
     * This method creates the default number of items and add them to the passed automobile
     * @param automobile
     */
    protected void addDefaultItems( Automobile automobile )
    {
        addItemByTypeIndex( ServerConstants.CONE, automobile );
        addItemByTypeIndex( ServerConstants.SIGN, automobile );
        addItemByTypeIndex( ServerConstants.DRUM, automobile );
        addItemByTypeIndex( ServerConstants.FLARE, automobile );
    }

    /**
     * This method creates the default number of items of type typeIndex
     * and adds them to the passed automobile
     * Type Index possible values can be found in ServerConstans=ts
     * @param typeIndex
     * @param automobile
     */
    protected void addItemByTypeIndex( int typeIndex, Automobile automobile )
    {
        // See if this is a valid typeIndex.
        if( typeIndex >= ServerConstants.ITEM_TYPE_NAME.length )
        {
            return;
        }

        int numberOfitems = ServerConstants.getDefaultNumberOfItemsByRole( typeIndex, automobile.getRoleId() );

        Item2D item2d;
        CreationAction action;
        Message message;
        try
        {
            for( int i = 0; i < numberOfitems; i++ )
            {
                item2d = new Item2D();
                item2d.setType( ServerConstants.ITEM_TYPE_NAME[typeIndex] );
                item2d.setWidth( ServerConstants.ITEM2D_DIMENSIONS[typeIndex].x );
                item2d.setHeight( ServerConstants.ITEM2D_DIMENSIONS[typeIndex].y );
                item2d.setOwner( automobile );

                automobile.putItem( item2d );
                gameSession.currentScenario.addItem( item2d );


                action = new CreationAction( item2d );
                message = new Message( Message.CREATE_MESSAGE, action );
                SendMessage( message );

                gameSession.addAction( action );
            }
        }
        catch( Exception ex )
        {

        }
    }
}
